package au.id.weston.scott.Sketchaetch;

// Copyright (c) 2009, Scott Weston <scott@weston.id.au>
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of Scott Weston nor the names of contributors may be
//       used to endorse or promote products derived from this software without
//       specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Random;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.Point;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.os.Vibrator;
import android.provider.MediaStore.Images.Media;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;

public class Sketchaetch extends Activity {

	private final static String TAG = "Sketchaetch";
	private final static String DUMP_PATH = "/sdcard/sketch-a-etch";
	public final static String PREFS_NAME = "SketchPrefs";

	private static Point prefsLoc = new Point();

	private final static int bumpCount = 3;
	private final static float viger = 3.0F;

	private float mAccelX = 0;
	private float mAccelY = 0;
	private float mAccelZ = 0;

	SketchView sketchView;

	private static MediaScannerConnection mScanner = null;
	private static Intent mCaptureImageIntent = null;
	private static ContentResolver contentResolver;

	private final static int IMAGE_SELECT_CALL = 1;

	private static int shakeitbaby = 0;
	
	private SensorManager myManager;
	private List<Sensor> sensors;
	private Sensor accSensor;

	private final SensorEventListener mySensorListener = new SensorEventListener()
	{
		public void onSensorChanged(SensorEvent event)
		{
			mAccelX = Math.abs(event.values[0])+1;
			mAccelY = Math.abs(event.values[1])+1;
			mAccelZ = event.values[2];
			if (mAccelZ < -7) {
				// ok the handset is upside down... now to detect shaking...
				if ((mAccelX*mAccelX + mAccelY*mAccelY) > viger) {
					shakeitbaby++;
				}
			} else {
				shakeitbaby = 0;
			}
			if (shakeitbaby > bumpCount) {
				final long[] pattern = {100L, 100L};
				Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
				vibrator.vibrate(pattern, -1);
				sketchView.realisticErase();
				sketchView.postInvalidate();
			}
		}

		public void onAccuracyChanged(Sensor sensor, int accuracy) {}
	};
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		Log.d(TAG, "onCreate");
		super.onCreate(savedInstanceState);

		// Set Sensor + Manager
		myManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
		sensors = myManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
		if(sensors.size() > 0)
		{
			accSensor = sensors.get(0);
		}

		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

		sketchView = new SketchView(this);
		sketchView.setId(1);
		setContentView(sketchView);

		// Restore preferences
		SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
		int x = settings.getInt("etchX", 0);
		int y = settings.getInt("etchY", 0);
		prefsLoc.x = x;
		prefsLoc.y = y;
		Log.d(TAG, "prefs etch point: x="+x+" y="+y);

		if (savedInstanceState != null) {
			Point p = new Point(savedInstanceState.getInt("x"), savedInstanceState.getInt("y"));
			Log.d(TAG, "state restore: " + p);
			sketchView.setEtchPoint(p);
		}

		mScanner = new MediaScannerConnection(this, 
				new MediaScannerConnection.MediaScannerConnectionClient() {
			public void onMediaScannerConnected() {
				Log.d(TAG, "MediaScannerConnected()");
			}
			public void onScanCompleted(String path, Uri uri) {
				Log.d(TAG, "onScanCompleted()");
			}
		}
		);

		contentResolver = getContentResolver();
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		Log.d(TAG, "onSaveInstanceState()");
		super.onSaveInstanceState(outState);
		outState.putInt("x", sketchView.etchPoint.x);
		outState.putInt("y", sketchView.etchPoint.y);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		Log.d(TAG, "oh a present for me?!  I hope it's tacos!");
		super.onActivityResult(requestCode, resultCode, data);
		mCaptureImageIntent = null;
		switch(requestCode){
		case IMAGE_SELECT_CALL:
			switch (resultCode) {
			case RESULT_OK:
				try {
					Uri dataUri = data.getData();
					if (sketchView.loadBackground(dataUri)) {
						Toast.makeText(this, "... loaded ...", Toast.LENGTH_SHORT).show();
					} else {
						Toast.makeText(this, "sorry but I have failed to load it", Toast.LENGTH_SHORT);
					}
				} catch (NullPointerException e) {
					Toast.makeText(this, "sorry but I have failed to load it", Toast.LENGTH_SHORT);
				}
			default:
				break;
			}
			break;
		} 
	}

	@Override
	protected void onStop() {
		Log.d(TAG, "onStop");
		myManager.unregisterListener(mySensorListener);
		mScanner.disconnect();
		super.onStop();
	}

	@Override
	protected void onStart() {
		Log.d(TAG, "onStart");
		super.onStart();
		mScanner.connect();
		sketchView.loadBitmap("sketch-auto-save.png");
	}

	@Override
	protected void onDestroy() {
		Log.d(TAG, "onDestroy");
		super.onDestroy();
	}

	@Override
	protected void onPause() {
		Log.d(TAG, "onPause");
		super.onPause();
		sketchView.saveBitmap("sketch-auto-save.png");
		SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
		SharedPreferences.Editor editor = settings.edit();
		editor.putInt("etchX", sketchView.etchPoint.x);
		editor.putInt("etchY", sketchView.etchPoint.y);
		editor.commit();
	}

	@Override
	protected void onResume() {
		Log.d(TAG, "onResume");
		super.onResume();
		myManager.registerListener(mySensorListener, accSensor, SensorManager.SENSOR_DELAY_UI);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		Log.d(TAG, "onCreateOptionsMenu");
		super.onCreateOptionsMenu(menu);
		MenuInflater inflater = new MenuInflater(this);
		inflater.inflate(R.layout.menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch(item.getItemId()) {
		case R.id.load:
			mCaptureImageIntent = new Intent("android.intent.action.GET_CONTENT");
			mCaptureImageIntent.setType("image/*");
			startActivityForResult(mCaptureImageIntent, IMAGE_SELECT_CALL);
			return true;
		case R.id.share:
			if (sketchView.saveBitmap("sketch-auto-save.png")) {
				Uri uri = Uri.fromFile(new File(DUMP_PATH + "/" + "sketch-auto-save.png"));

				//				OutputStream outStream = getContentResolver().openOutputStream(uri);
				//				sketchView.mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
				//				outStream.close();
				//				String url = Images.Media.insertImage(getContentResolver(), sketchView.mBitmap, "title", null);

				Intent mSentImageIntent = new Intent(Intent.ACTION_SEND);
				mSentImageIntent.setType("image/png");
				mSentImageIntent.putExtra(Intent.EXTRA_STREAM, uri);
				mSentImageIntent.putExtra(Intent.EXTRA_SUBJECT, "My sketch");
				startActivity(Intent.createChooser(mSentImageIntent, "Choose a share method"));
			} else {
				Toast.makeText(this, "sharing failed, sorry :(", Toast.LENGTH_LONG).show();
				Log.d(TAG, "share save failed");
			}
			return true;
		case R.id.save:
			Long now = Long.valueOf(System.currentTimeMillis()/1000);
			if (sketchView.saveBitmap(String.format("sketch%d.png", now))) {
				Toast.makeText(this, "I made it myself! (saved)", Toast.LENGTH_SHORT).show();
			}
			return true;
		case R.id.erase:
			final long[] pattern = {0L, 250L};
			Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
			vibrator.vibrate(pattern, -1);
			sketchView.setupEtcher();
			sketchView.postInvalidate();
			return true;
		case R.id.buy:
			startActivity(new Intent().setAction(Intent.ACTION_VIEW).setData(Uri.parse("http://www.amazon.com/gp/aw/s.html/?m=aps&k=etch-a-sketch&tag=scotwest-20")));
			return true;
		case R.id.about:
			new AlertDialog.Builder(this)
			.setIcon(R.drawable.sketch_a_etch)
			.setTitle("Sketch A Etch")
			.setMessage(R.string.about)
			.setPositiveButton("ok", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int whichButton) {
					/* User clicked OK  */
				}
			})
			.create().show();

			return true;
		}
		return false;
	}

	// ---------------------------------------------------------------------------------------------
	// ---------------------------------------------------------------------------------------------
	// ---------------------------------------------------------------------------------------------

	private static class SketchView extends View {

		// The etch marker
		public Point etchPoint;

		// The various paints and effects that are used
		private Paint paint;
		private Paint knobs;
		private Paint border;
		private Paint marker;
		private Paint mPaintText;
		private Paint mPaintTextSm;
		private Paint[] eraser = new Paint[4];
		private PathEffect borderEffect;

		// Our canvas and bitmap that gets totally messed with
		private Canvas mCanvas = null;
		private Bitmap mBitmap = null;
		private Bitmap mRestor = null;

		private float oldX1 = -1f;
		private float oldX2 = -1f;

		public SketchView(Context context) {
			super(context);
			Log.d(TAG, "SketchView(): constructor");

			setFocusable(true);
			setFocusableInTouchMode(true);

			// Mix all our paints
			paint = new Paint(Paint.ANTI_ALIAS_FLAG);
			paint.setStyle(Paint.Style.STROKE);
			paint.setStrokeWidth(1);
			paint.setColor(0xff444444);

			marker = new Paint(Paint.ANTI_ALIAS_FLAG);
			marker.setStyle(Paint.Style.STROKE);
			marker.setStrokeWidth(4);
			marker.setColor(0xff000000);

			knobs = new Paint(Paint.ANTI_ALIAS_FLAG);
			knobs.setStyle(Paint.Style.FILL);
			knobs.setStrokeWidth(1);
			knobs.setColor(0xffcccccc);

			borderEffect = new CornerPathEffect(80);
			border = new Paint(Paint.ANTI_ALIAS_FLAG);
			border.setStyle(Paint.Style.STROKE);
			border.setStrokeWidth(100);
			border.setColor(0xff990d16);
			border.setPathEffect(borderEffect);

			mPaintText = new Paint(Paint.LINEAR_TEXT_FLAG);
			mPaintText.setStyle(Paint.Style.STROKE);
			mPaintText.setAntiAlias(true);
			mPaintText.setTextSize(34);
			mPaintText.setColor(Color.WHITE);

			mPaintTextSm = new Paint(Paint.LINEAR_TEXT_FLAG);
			mPaintTextSm.setStyle(Paint.Style.STROKE);
			mPaintTextSm.setAntiAlias(true);
			mPaintTextSm.setTextSize(12);
			mPaintTextSm.setColor(0x80ffffff);
		}

		public void realisticErase() {
			Log.d(TAG, "realisticErase()");
			playSoundEffect(SoundEffectConstants.CLICK);

			for (int i = 0; i < eraser.length; i++) {
				Random generator = new Random();
				eraser[i] = new Paint();
				eraser[i].setStyle(Paint.Style.FILL);
				int[] eraseCols = {0x00cccccc, Color.LTGRAY};
				RadialGradient eraseGradient = new RadialGradient(generator.nextInt(480), generator.nextInt(320), 100,
						eraseCols, null, Shader.TileMode.MIRROR);
				eraser[i].setShader(eraseGradient);
			}

			for (int i = 0; i < eraser.length; i++) {
				mCanvas.drawRect(new Rect(55, 55, getWidth()-55, getHeight()-55), eraser[i]);
			}
			shakeitbaby = 0;
		}

		public void setEtchPoint(Point p) {
			Log.d(TAG, "setEtchPoint()");
			if (p != null) {
				if (p.x > 55 && p.y > 55 & p.x < 425 && p.y < 365) {
					etchPoint = new Point(p);
					return;
				}
			}
			etchPoint = new Point(240, 160);
		}

		public boolean saveBitmap(String name) {
			Log.d(TAG, "saveBitmap(): " + name);

			try {
				File dir = new File(DUMP_PATH);
				dir.mkdirs();

				String savefile = String.format("%s/%s", DUMP_PATH, name);
				OutputStream os = new FileOutputStream(savefile);
				mBitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
				os.flush();
				os.close();

				mScanner.connect();
				mScanner.scanFile(savefile, "image/png");
				Log.d(TAG, "bitmap saved: " + savefile);
				return true;
			} catch (Exception e) {
				Log.e(TAG, "bitmap save has failed: e=" + e);
				return false;
			}
		}

		@Override
		protected void onSizeChanged(int w, int h, int oldw, int oldh) {
			Log.d(TAG, String.format("onSizeChanged(%d, %d, %d, %d)", w, h, oldw, oldh));

			if (etchPoint == null) {
				setEtchPoint(prefsLoc);
			}

			if (mBitmap == null || mRestor != null ) {
				initSketcher(w, h, mRestor);
			}
			
			Log.d(TAG, "onSizeChanged:done");
		}

		@Override
		protected void onDraw(Canvas canvas) {
			// Draw and animate the motion wheels
			mCanvas.drawCircle(30, mCanvas.getHeight() - 30, 20, knobs);
			mCanvas.drawCircle(mCanvas.getWidth() - 30, mCanvas.getHeight() - 30, 20, knobs);
			mCanvas.drawArc(new RectF(15, mCanvas.getHeight() - 45, 45, mCanvas.getHeight() - 15), 
					etchPoint == null ? 0 : etchPoint.x*5, 
							30F, 
							false,
							marker);
			mCanvas.drawArc(new RectF(mCanvas.getWidth() - 45, mCanvas.getHeight() - 45, mCanvas.getWidth() - 15, mCanvas.getHeight() - 15), 
					etchPoint == null ? 0 : etchPoint.y*5, 
							30F, 
							false,
							marker);

			// draw our private bitmap onto the real canvas
			if (mBitmap != null) {
				canvas.drawBitmap(mBitmap, 0, 0, null);
			}

			// do some garbage collection
			if (mRestor != null) {
				mRestor.recycle();
				mRestor = null;
			}
		}

		void loadBitmap(String file) {
			Log.d(TAG, "loadBitmap(): " + file);
			// if something is already loaded then don't load in a new one
			if (mRestor != null) return;
			try {
				Log.d(TAG, "mRestor is free, Loading: " + file);
				mRestor = Media.getBitmap(contentResolver, Uri.parse("file://" + DUMP_PATH + "/" + file));
			} catch (Exception e) {
				Log.d(TAG, "unable to restore bitmap, first run?");
			}
		}

		boolean loadBackground(Uri uri) {
			Log.d(TAG, "loadBackground(): " + uri.toString());
			try {
				if (mRestor != null) {
					mRestor.recycle();
					mRestor = null;
				}
				mRestor = Media.getBitmap(contentResolver, uri);
				initSketcher(getWidth(), getHeight(), mRestor);
				return true;
			} catch (IOException e) {
				return false;
			}
		}

		boolean loadBackground(String file) {
			return loadBackground(Uri.parse("file://" + DUMP_PATH + "/" + file));
		}

		boolean initSketcher(int w, int h, Bitmap load) {
			Log.d(TAG, "initSketcher()");
			playSoundEffect(SoundEffectConstants.CLICK);

			Bitmap newBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
			Canvas newCanvas = new Canvas();
			newCanvas.setBitmap(newBitmap);
			mBitmap = newBitmap;
			mCanvas = newCanvas;

			// draw the paths that make up our sketch-a-etch borders
			setupEtcher();

			// copy in the load bitmap
			if (load != null) {
				Log.d(TAG, "initSketcher(): load in picture");
				int srcWidth = load.getWidth();
				int srcHeight = load.getHeight();
				Rect dest = new Rect(55, 55, getWidth()-55, getHeight()-55);
				Rect src = new Rect(55, 55, srcWidth-55, srcHeight-55);
				mCanvas.drawBitmap(load, src, dest, null);
			}
			
			int[] lGradColours = {0xff400000, 0xffaa0d16, 0xff990d16, 0xff400000};
			float[] lGradProps = {0.0f, 0.05f, 0.95f, 1.0f};
			LinearGradient lGradient = new LinearGradient(0, 0, getWidth(), 0, lGradColours, lGradProps, Shader.TileMode.MIRROR);
			border.setShader(lGradient);
			
			// put the etch point on the screen
			mCanvas.drawCircle(etchPoint.x, etchPoint.y, 1.0F, paint);
			invalidate();
			return true;
		}

		private void setupEtcher() {
			Log.d(TAG, "setupSketcher()");
			Path p = new Path();

			p.moveTo(0, 0);
			p.lineTo(0, mCanvas.getHeight());
			p.lineTo(mCanvas.getWidth(), mCanvas.getHeight());
			p.lineTo(mCanvas.getWidth(), 0);
			p.close();

			mCanvas.drawColor(Color.LTGRAY);
			mCanvas.drawPath(p, border);

			Rect rect = new Rect();

			mPaintText.getTextBounds("Sketch A Etch", 0, "Sketch A Etch".length(), rect);
			mCanvas.drawText("Sketch A Etch", ((mCanvas.getWidth()/2)-(rect.right/2)), 40, mPaintText);

			mPaintTextSm.getTextBounds("use the trackball to draw.  click the menu to save/load/etc.", 
					0, "use the trackball to draw.  click the menu to save/load/etc.".length(), rect);
			mCanvas.drawText("use the trackball to draw.  click the menu to save/load/etc.", ((mCanvas.getWidth()/2)-(rect.right/2)), mCanvas.getHeight()-37, mPaintTextSm);

			final String[] notes = { "http://sketch-a-etch.googlecode.com/", 
					"written by Scott Weston",
			"Gir: awwww, I wanted to explode" };
			Random generator = new Random();
			int rnd = generator.nextInt(notes.length);
			String note = notes[rnd];
			mPaintTextSm.getTextBounds(note, 0, note.length(), rect);
			mCanvas.drawText(note, (mCanvas.getWidth()/2)-(rect.right/2), mCanvas.getHeight()-10, mPaintTextSm);
		}


		@Override
		public boolean onTrackballEvent(MotionEvent event) {
			super.onTrackballEvent(event);

			// draw marks on the screen
			int N = event.getHistorySize();
			final float scaleX = event.getXPrecision();
			final float scaleY = event.getYPrecision();

			Path p = new Path();
			// etchPoint is set from last call here, or onCreate
			if (etchPoint != null) {
				etchPoint = chockPoints(etchPoint);
			} else {
				setEtchPoint(prefsLoc);
			}
			p.moveTo(etchPoint.x, etchPoint.y);

			for (int i = 0; i < N; i++) {
				etchPoint.x += event.getHistoricalX(i)*scaleX;
				etchPoint.y += event.getHistoricalY(i)*scaleY;
				etchPoint = chockPoints(etchPoint);
				p.lineTo(etchPoint.x, etchPoint.y);
			}
			etchPoint.x += event.getX()*scaleX;
			etchPoint.y += event.getY()*scaleY;
			etchPoint = chockPoints(etchPoint);
			p.lineTo(etchPoint.x, etchPoint.y);
			mCanvas.drawPath(p, paint);
			this.invalidate();
			return true;
		}

		@Override
		public boolean onKeyDown(int keyCode, KeyEvent event) {
			if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_UP ||
					keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
				Path p = new Path();

				// etchPoint is set from last call here, or onCreate
				if (etchPoint != null) {
					p.moveTo(etchPoint.x, etchPoint.y);
					etchPoint = chockPoints(etchPoint);
				} else {
					p.moveTo(getWidth()/2, getHeight()/2);
				}

				switch(keyCode) {
				case KeyEvent.KEYCODE_DPAD_UP:
					etchPoint.y -= 2;
					break;
				case KeyEvent.KEYCODE_DPAD_DOWN:
					etchPoint.y += 2;
					break;
				case KeyEvent.KEYCODE_DPAD_LEFT:
					etchPoint.x -= 2;
					break;
				case KeyEvent.KEYCODE_DPAD_RIGHT:
					etchPoint.x += 2;
					break;
				}
				etchPoint = chockPoints(etchPoint);
				p.lineTo(etchPoint.x, etchPoint.y);
				mCanvas.drawPath(p, paint);
				this.invalidate();
				return true;
			} else {
				return super.onKeyDown(keyCode, event);
			}
		}


		private Point chockPoints(Point p) {
			if (p.x < 55) {
				p.x = 55;
			}
			if (p.x > (mCanvas.getWidth()-55)) {
				p.x = mCanvas.getWidth()-55;
			}
			if (p.y < 55) {
				p.y = 55;
			}
			if (p.y > (mCanvas.getHeight()-55)) {
				p.y = mCanvas.getHeight()-55;
			}
			return p;
		}

		@Override
		public boolean onTouchEvent(MotionEvent event) {
			int pointerCount = event.getPointerCount();

			if (event.getAction() == MotionEvent.ACTION_DOWN || 
					event.getAction() == MotionEvent.ACTION_UP ||
					event.getAction() == MotionEvent.ACTION_POINTER_1_DOWN ||
					event.getAction() == MotionEvent.ACTION_POINTER_2_DOWN ||
					event.getAction() == MotionEvent.ACTION_POINTER_1_UP ||
					event.getAction() == MotionEvent.ACTION_POINTER_2_UP) {
				Log.d(TAG, "TOUCH: action down: " + pointerCount);
				if (pointerCount == 1)
				{
					oldX1 = event.getX(0);
					oldX2 = event.getX(0);
				}
				if (pointerCount == 2)
				{
					oldX1 = event.getX(0);
					oldX2 = event.getX(1);
				}
			} else if (event.getAction() == MotionEvent.ACTION_MOVE) {

				Path p = new Path();

				// etchPoint is set from last call here, or onCreate
				if (etchPoint != null) {
					p.moveTo(etchPoint.x, etchPoint.y);
					etchPoint = chockPoints(etchPoint);
				} else {
					p.moveTo(getWidth()/2, getHeight()/2);
				}

				if (pointerCount == 1) {
					if (event.getX(0) > (getWidth()/2)) {
						// up/down
						int diff = (int)(event.getX(0) - oldX1);
						if (Math.abs(diff) < 20) etchPoint.y -= diff;
					}
					else if (event.getX(0) < (getWidth()/2)) {
						// left/right
						int diff = (int)(event.getX(0) - oldX1);
						if (Math.abs(diff) < 20) etchPoint.x += diff;
					}
					oldX1 = event.getX(0);
				} else if (pointerCount == 2) {
					if (event.getX(0) > (getWidth()/2)) {
						// up/down
						int diff = (int)(event.getX(0) - oldX1);
						if (Math.abs(diff) < 20) etchPoint.y -= diff;
					}
					if (event.getX(0) < (getWidth()/2)) {
						// left/right
						int diff = (int)(event.getX(0) - oldX1);
						if (Math.abs(diff) < 20) etchPoint.x += diff;
					}
					oldX1 = event.getX(0);

					if (event.getX(1) > (getWidth()/2)) {
						// up/down
						int diff = (int)(event.getX(1) - oldX2);
						if (Math.abs(diff) < 20) etchPoint.y -= diff;
					}
					if (event.getX(1) < (getWidth()/2)) {
						// left/right
						int diff = (int)(event.getX(1) - oldX2);
						if (Math.abs(diff) < 20) etchPoint.x += diff;
					}
					oldX2 = event.getX(1);
				}
				etchPoint = chockPoints(etchPoint);
				p.lineTo(etchPoint.x, etchPoint.y);
				mCanvas.drawPath(p, paint);
				this.invalidate();
			}
			return true;
		}
	}

	//	Point t = new Point();
	//	public boolean onTouch(View v, MotionEvent event) {
	//		if(event.getAction() == MotionEvent.ACTION_DOWN){
	//			t.x = (int) event.getX();
	//			t.y = (int) event.getY();
	//		}else if(event.getAction() == MotionEvent.ACTION_MOVE){
	//			Path p = new Path();
	//			// etchPoint is set from last call here, or onCreate
	//			if (sketchView.etchPoint != null) {
	//				p.moveTo(sketchView.etchPoint.x, sketchView.etchPoint.y);
	//				sketchView.etchPoint = sketchView.chockPoints(sketchView.etchPoint);
	//			} else {
	//				p.moveTo(sketchView.getWidth()/2, sketchView.getHeight()/2);
	//			}
	//			sketchView.etchPoint.x += event.getX() - t.x;
	//			sketchView.etchPoint.y += event.getY() - t.y;
	//			sketchView.etchPoint = sketchView.chockPoints(sketchView.etchPoint);
	//			p.lineTo(sketchView.etchPoint.x, sketchView.etchPoint.y);
	//			sketchView.mCanvas.drawPath(p, sketchView.paint);
	//			this.sketchView.invalidate();
	//			return true;
	//		}
	//		return false;
	//	}
}
